
// nb_timer_pwm.c

/*
 *  reference https://github.com/SiliconLabs/peripheral_examples/blob/master/series2/timer/timer_pwm_interrupt/src/main.c
 *
 *
 *
 */

#include "em_device.h"
#include "em_cmu.h"
#include "em_emu.h"
#include "em_chip.h"
#include "em_gpio.h"
#include "em_timer.h"

#include "newbit/nb_timer.h"
#include "newbit/nb_hal.h"

// Note: change this to set the desired output frequency in Hz
#define PWM_FREQ          1000
#define DUTY_CYCLE_STEPS  0.3


static uint32_t topValue = 0;
static volatile float dutyCycle = 0;

/**************************************************************************//**
 * @brief
 *    Interrupt handler for TIMER_USED that changes the duty cycle
 *
 * @note
 *    This handler doesn't actually dynamically change the duty cycle. Instead,
 *    it acts as a template for doing so. Simply change the dutyCyclePercent
 *    global variable here to dynamically change the duty cycle.
 *****************************************************************************/
void TIMER1_IRQHandler(void)
{
  // Acknowledge the interrupt
  uint32_t flags = TIMER_IntGet(TIMER_USED);
  TIMER_IntClear(TIMER_USED, flags);


  if ( flags & TIMER_IF_OF)
  // Update CCVB to alter duty cycle starting next period
	  TIMER_CompareBufSet(TIMER_USED, 0, (uint32_t)(topValue * dutyCycle));

  /*
  if ( flags & TIMER_IF_CC1)
	  TIMER_CompareBufSet(TIMER_USED, 1, (uint32_t)(topValue * 0.5));
	  */
}

// timer clock enable
static void nb_clock_init(void)
{
	CMU_ClockSelectSet(CMU_CLOCK_TIMER_USED ,  cmuSelect_HFXO );

	CMU_ClockEnable(CMU_CLOCK_TIMER_USED, true);
}

// timer configuration
static void nb_timer_pwm_init(void)
{
	  uint32_t timerFreq = 0;
	  // Initialize the timer
	  TIMER_Init_TypeDef timerInit = TIMER_INIT_DEFAULT;
	  // Configure TIMER_USED Compare/Capture for output compare
	  TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT;

	  // Use PWM mode, which sets output on overflow and clears on compare events
	  timerInit.prescale = timerPrescale64;
	  timerInit.enable = false;
	  timerCCInit.mode = timerCCModePWM;

	  // Configure but do not start the timer
	  TIMER_Init(TIMER_USED, &timerInit);

	  // Route Timer0 CCn output to PC
	  GPIO->TIMERROUTE[TIMER_INDEX_0].ROUTEEN  = GPIO_TIMER_ROUTEEN_CC0PEN;
	  GPIO->TIMERROUTE[TIMER_INDEX_0].CC0ROUTE = (TIMER_USED_GPIO_PORT_0 << _GPIO_TIMER_CC0ROUTE_PORT_SHIFT)
	  								  | (TIMER_USED_GPIO_PIN_0 << _GPIO_TIMER_CC0ROUTE_PIN_SHIFT);


	  GPIO->TIMERROUTE[TIMER_INDEX_0].ROUTEEN  |= GPIO_TIMER_ROUTEEN_CC1PEN;
	  GPIO->TIMERROUTE[TIMER_INDEX_0].CC1ROUTE = (TIMER_USED_GPIO_PORT_1 << _GPIO_TIMER_CC1ROUTE_PORT_SHIFT)
	  								  | (TIMER_USED_GPIO_PIN_1 << _GPIO_TIMER_CC1ROUTE_PIN_SHIFT);

	  // Configure CC Channel 0
	  TIMER_InitCC(TIMER_USED, 0, &timerCCInit);
	  // Configure CC Channel 1
	  TIMER_InitCC(TIMER_USED, 1, &timerCCInit);

	  // Start with 10% duty cycle
	  dutyCycle = DUTY_CYCLE_STEPS;

	  // set PWM period
	  timerFreq = CMU_ClockFreqGet(CMU_CLOCK_TIMER_USED) / (timerInit.prescale + 1);
	  topValue = (timerFreq / PWM_FREQ);
	  // Set top value to overflow at the desired PWM_FREQ frequency
	  TIMER_TopSet(TIMER_USED, topValue);

	  // Set compare value for initial duty cycle
	  TIMER_CompareSet(TIMER_USED, 0, (uint32_t)(topValue * dutyCycle));

	  TIMER_CompareSet(TIMER_USED, 1, (uint32_t)(topValue * 0.5));

	  // Start the timer
	  TIMER_Enable(TIMER_USED, true);

	  // Enable TIMER_USED compare event interrupts to update the duty cycle
	  TIMER_IntEnable(TIMER_USED, TIMER_IEN_CC0);
	  //TIMER_IntEnable(TIMER_USED, TIMER_IEN_CC1);

	  //NVIC_EnableIRQ(TIMER1_IRQn);
}



// timer inital & start , the main of those code
// normally, call in emberAfMainInitCallback();
void nb_start_running_xx(void)
{
	nb_gpio_init();
	nb_clock_init();
	nb_timer_pwm_init();
}
